home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pp / pp-6.0 / Chans / smtp / sm_wtmail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-18  |  13.1 KB  |  604 lines

  1. /* sm_wtmail:  mail-commands for smtp mail */
  2.  
  3. # ifndef lint
  4. static char Rcsid[] = "@(#)$Header: /xtel/pp/pp-beta/Chans/smtp/RCS/sm_wtmail.c,v 6.0 1991/12/18 20:12:19 jpo Rel $";
  5. # endif
  6.  
  7. /*
  8.  * $Header: /xtel/pp/pp-beta/Chans/smtp/RCS/sm_wtmail.c,v 6.0 1991/12/18 20:12:19 jpo Rel $
  9.  *
  10.  * $Log: sm_wtmail.c,v $
  11.  * Revision 6.0  1991/12/18  20:12:19  jpo
  12.  * Release 6.0
  13.  *
  14.  */
  15.  
  16.  
  17.  
  18. #include "util.h"
  19. #include "retcode.h"
  20. #include "chan.h"
  21. #include <signal.h>
  22. #include <isode/internet.h>
  23. #include "ap.h"
  24.  
  25. extern CHAN    *chanptr;
  26. extern char    *loc_dom_mta;
  27. extern char    *strdup();
  28.  
  29. extern    char    *open_host;
  30.  
  31. struct sm_rstruct        /* save last reply obtained */
  32. {                  /* for holding a net reply */
  33.     int        sm_rval;          /* rp.h value for reply */
  34.     int        sm_rlen;          /* current lengh of reply string */
  35.     char    sm_rgot;          /* true, if have a reply */
  36.     char    sm_rstr[LINESIZE];      /* human-oriented reply text */
  37. } sm_rp;
  38. #define smtp_error(def) (sm_rp.sm_rgot ? sm_rp.sm_rstr : (def))
  39.  
  40. static CHAN    *sm_chptr;    /* structure for channel that we are */
  41. FILE    *sm_rfp, *sm_wfp;
  42. static char    sm_rnotext[] = "No reply text given";
  43. static int sm_rrec ();
  44.  
  45. #define SM_HTIME    180    /* Time allowed for HELO command */
  46. #define SM_OTIME    300    /* Time allowed for a netopen */
  47. #define SM_ATIME    120    /* Time allowed for answer after open */
  48. #define SM_STIME    300    /* Time allowed for MAIL command */
  49. #define SM_TTIME    300    /* Time allowed for RCPT command */
  50. #define SM_DTIME        120    /* Time allowed for a DATA command */
  51. #define SM_PTIME        600    /* Time allowed for the "." command */
  52.  
  53. #define SM_QTIME    60    /* Time allowed for QUIT command */
  54.  
  55. int    data_bytes = 0;
  56.  
  57. #if sparc && defined(__GNUC__)    /* work around bug in gcc 1.37 sparc version */
  58. #define inet_ntoa myinet_ntoa
  59.  
  60. static char *myinet_ntoa (in)
  61. struct in_addr in;
  62. {
  63.     static char buf[80];
  64.  
  65.     (void) sprintf (buf, "%d.%d.%d.%d",
  66.             (in.s_addr >> 24) & 0xff,
  67.             (in.s_addr >> 16) & 0xff,
  68.             (in.s_addr >> 8 ) & 0xff,
  69.             (in.s_addr    ) & 0xff);
  70.     return buf;
  71. }
  72. #else
  73. extern char    *inet_ntoa ();
  74. #endif
  75.  
  76. sm_wfrom (sender)
  77. char    *sender;
  78. {
  79.     char    linebuf[LINESIZE];
  80.  
  81.     (void) sprintf (linebuf, "MAIL FROM:<%s>", sender);
  82.     if (rp_isbad (sm_cmd (linebuf, SM_STIME)))
  83.         return (RP_DHST);
  84.  
  85.     switch( sm_rp.sm_rval ) {
  86.         case 250:
  87.         break;        /* We're off and running! */
  88.  
  89.         case 500:
  90.         case 501:
  91.         case 552:
  92.         return RP_PARM;
  93.  
  94.         case 421:
  95.         case 450:
  96.         case 451:
  97.         case 452:
  98.         return RP_AGN;
  99.  
  100.         default:
  101.         return RP_BHST;
  102.     }
  103.     return RP_OK;
  104. }
  105.  
  106. sm_wto (adr)         /* send one address spec to local */
  107. char    adr[];              /* rest of address */
  108. {
  109.     char linebuf[LINESIZE];
  110.  
  111.     if( isstr(adr))
  112.         (void) sprintf (linebuf, "RCPT TO:<%s>", adr);
  113.     else
  114.         (void) strcpy(linebuf, "RCPT TO:<>");
  115.  
  116.     if (rp_isbad (sm_cmd (linebuf, SM_TTIME)))
  117.         return (RP_DHST);
  118.  
  119.     switch (sm_rp.sm_rval)
  120.     {
  121.         case 250:
  122.         case 251:
  123.         return RP_AOK;
  124.  
  125.         case 421:
  126.         case 450:
  127.         case 451:
  128.         case 452:
  129.         return RP_AGN;
  130.  
  131.         case 550:
  132.         case 551:
  133.         case 552:
  134.         case 553:
  135.         case 554:        /* BOGUS: sendmail is out of spec! */
  136.         return RP_USER;
  137.  
  138.         case 500:
  139.         case 501:
  140.         return RP_PARM;
  141.  
  142.     }
  143.     return RP_RPLY;
  144.  
  145. }
  146.  
  147. sm_wrdata ()
  148. {
  149.     if (rp_isbad (sm_cmd ("DATA", SM_DTIME)))
  150.         return RP_DHST;
  151.  
  152.     switch (sm_rp.sm_rval) {
  153.         case 354:
  154.         return RP_OK;
  155.  
  156.         case 500:
  157.         case 501:
  158.         case 503:
  159.         case 554:
  160.         return RP_NDEL;
  161.  
  162.         case 421:
  163.         case 451:
  164.         return RP_AGN;
  165.  
  166.     }
  167.     return RP_RPLY;
  168. }
  169.  
  170. sm_wrdot (naddr)
  171. int naddr;
  172. {
  173.     if (rp_isbad (sm_cmd (".", SM_PTIME + (3 * naddr))))
  174.         return RP_DHST;
  175.  
  176.     switch (sm_rp.sm_rval) {
  177.         case 250:
  178.         case 251:
  179.         return RP_OK;
  180.  
  181.         case 552:
  182.         case 554:
  183.         return RP_NDEL;
  184.  
  185.         case 421:
  186.         case 451:
  187.         case 452:
  188.         return RP_AGN;
  189.     }    
  190.     return RP_AGN;
  191. }
  192.         
  193.  
  194. sm_init (curchan)          /* session initialization */
  195. CHAN *curchan;              /* name of channel */
  196. {
  197.     sm_chptr = curchan;
  198.     /* phs_note (sm_chptr, PHS_CNSTRT); */
  199.     return (RP_OK);          /* generally, a no-op */
  200. }
  201.  
  202.  
  203. static
  204.     sm_irdrply ()          /* get net reply & stuff into sm_rp */
  205. {
  206.     static char sep[] = "; ";      /* for sticking multi-lines together */
  207.     short     len,
  208.         tmpreply,
  209.         retval;
  210.     char    linebuf[LINESIZE];
  211.     char    tmpmore;
  212.     register char  *linestrt;      /* to bypass bad initial chars in buf */
  213.     register short    i;
  214.     register char   more;      /* are there continuation lines? */
  215.  
  216. newrply:
  217.     for (more = FALSE, sm_rp.sm_rgot = FALSE, sm_rp.sm_rlen = 0;
  218.         rp_isgood (retval = sm_rrec (linebuf, sizeof linebuf, &len));)
  219.     {
  220.         PP_LOG (LLOG_PDUS, ("<- %s", linebuf));
  221.         /* 1st col in linebuf gets reply code */
  222.         for (linestrt = linebuf; /* skip leading baddies, probably */
  223.          len > 0 &&    /*  from a lousy Multics */
  224.          (!isascii ((char) *linestrt) ||
  225.           !isdigit ((char) *linestrt));
  226.          linestrt++, len--)
  227.             continue;
  228.  
  229.         tmpmore = FALSE;    /* start fresh */
  230.         tmpreply = atoi (linestrt);
  231.         if ((len -= 3) > 0)
  232.         {
  233.             linestrt += 3;
  234.             if (len > 0 && *linestrt == '-')
  235.             {
  236.                 tmpmore = TRUE;
  237.                 linestrt++;
  238.                 if (--len > 0)
  239.                     for (; len > 0 && isspace (*linestrt); linestrt++, len--)
  240.                         continue;
  241.             }
  242.         }
  243.  
  244.         if (more)        /* save reply value from 1st line */
  245.         {            /* we at end of continued reply? */
  246.             if (tmpreply != sm_rp.sm_rval || tmpmore)
  247.                 continue;
  248.             more = FALSE; /* end of continuation */
  249.         }
  250.         else        /* not in continuation state */
  251.         {
  252.             sm_rp.sm_rval = tmpreply;
  253.  
  254.             more = tmpmore; /* more lines to follow? */
  255.  
  256.             if (len <= 0)
  257.             {        /* fake it, if no text given */
  258.                 bcopy (sm_rnotext, linestrt = linebuf,
  259.                    (sizeof sm_rnotext) - 1);
  260.                 len = (sizeof sm_rnotext) - 1;
  261.             }
  262.         }
  263.  
  264.         if ((i = MIN (len, (LINESIZE - 1) - sm_rp.sm_rlen)) > 0)
  265.         {            /* if room left, save the human text */
  266.             bcopy (linestrt, &sm_rp.sm_rstr[sm_rp.sm_rlen], i);
  267.             sm_rp.sm_rlen += i;
  268.             sm_rp.sm_rstr[sm_rp.sm_rlen] = 0;
  269.             if (more && sm_rp.sm_rlen < (LINESIZE - 4))
  270.             {        /* put a separator between lines */
  271.                 bcopy (sep, &(sm_rp.sm_rstr[sm_rp.sm_rlen]), (sizeof sep));
  272.                 sm_rp.sm_rlen += (sizeof sep) - 1;
  273.             }
  274.         }
  275.  
  276.         if (!more)
  277.         {
  278.             if (sm_rp.sm_rval < 100)
  279.                 goto newrply; /* skip info messages */
  280.  
  281.             sm_rp.sm_rgot = TRUE;
  282.             return (RP_OK);
  283.         }
  284.     }
  285.     return (retval);          /* error return */
  286. }
  287.  
  288.  
  289. static sm_rrec (linebuf, blen, len)    /* read a reply record from net */
  290. char   *linebuf;          /* where to stuff text */
  291. int    blen;
  292. short     *len;                /* where to stuff length */
  293. {
  294.     extern int errno;
  295.     int c, n;
  296.     char *cp;
  297.  
  298.     *len = 0;              /* for clean logging if nothing read */
  299.     linebuf[0] = '\0';
  300.  
  301.     for (n = blen, cp = linebuf; n > 0;) {
  302.         if ((c = getc (sm_rfp)) == EOF || c == '\n')
  303.             break;
  304.         *cp ++ = c;
  305.         n --;
  306.     }
  307.     *cp = '\0';
  308.     *len = blen - n;
  309.  
  310.     if (ferror (sm_rfp) || feof (sm_rfp))
  311.     {                  /* error or unexpected eof */
  312.     PP_LOG (LLOG_EXCEPTIONS,
  313.         ("netread:  ret=%d, fd=%d", *len, fileno (sm_rfp)));
  314.     sm_nclose (NOTOK);       /* since it won't work anymore */
  315.     return (RP_BHST);
  316.     }
  317.     if (n <= 0)
  318.     {
  319.     PP_LOG (LLOG_EXCEPTIONS, ("net input overflow"));
  320.     while (getc (sm_rfp) != '\n'
  321.         && !ferror (sm_rfp) && !feof (sm_rfp))
  322.         continue;
  323.     }
  324.     else
  325.     if (linebuf[*len - 1] == '\r')
  326.         *len -= 1;          /* get rid of crlf or just lf */
  327.  
  328.     return (RP_OK);
  329. }
  330.  
  331. sm_cmd (cmd, time)        /* Send a command */
  332. char    *cmd;
  333. int    time;            /* Max time for sending and getting reply */
  334. {
  335.     short     retval;
  336.  
  337.     PP_LOG (LLOG_PDUS, ("-> %s", cmd));
  338.  
  339.     if (sm_wfp == NULL)
  340.     return sm_rp.sm_rval = RP_DHST;
  341.     if (timeout((unsigned)time)) {
  342.     PP_TRACE (("sm_cmd(): host died?"));
  343.     sm_nclose (NOTOK);
  344.     return (sm_rp.sm_rval = RP_DHST);
  345.     }
  346.     fprintf (sm_wfp, "%s\r\n", cmd);
  347.     if (!ferror (sm_wfp))
  348.         (void) fflush (sm_wfp);
  349.  
  350.     if (ferror (sm_wfp))
  351.     {
  352.     PP_TRACE (("sm_cmd(): host died?"));
  353.     sm_nclose (NOTOK);
  354.     return (sm_rp.sm_rval = RP_DHST);
  355.     }
  356.  
  357.     if (rp_isbad (retval = sm_irdrply ()))
  358.     return( sm_rp.sm_rval = retval );
  359.  
  360.     timeout (0);
  361.     return RP_OK;
  362. }
  363.  
  364. sm_wstm (buf, len)          /* write some message text out */
  365. char    *buf;              /* what to write */
  366. register int    len;          /* how long it is */
  367. {
  368.     static char lastchar = 0;
  369.     short      retval;
  370.     register char  *bufptr;
  371.     register char    newline;
  372.  
  373.     if (buf == 0 && len == 0) { /* end of text */
  374.         if (lastchar != '\n') {    /* make sure it ends cleanly */
  375.             fputs ("\r\n", sm_wfp);
  376.             data_bytes += 2;
  377.         }
  378.         if (ferror (sm_wfp))
  379.             return (RP_DHST);
  380.         lastchar = 0;    /* reset for next message */
  381.         retval = RP_OK;
  382.     }
  383.     else
  384.     {
  385.         newline = (lastchar == '\n') ? TRUE : FALSE;
  386.         for (bufptr = buf; len--; bufptr++)
  387.         {
  388.             switch (*bufptr) /* cycle through the buffer */
  389.             {
  390.                 case '\n': /* Telnet requires crlf */
  391.                 newline = TRUE;
  392.                 putc ('\r', sm_wfp);
  393.                 data_bytes ++;
  394.                 break;
  395.  
  396.                 case '.': /* Insert extra period at beginning */
  397.                 if (newline) {
  398.                     putc ('.', sm_wfp);
  399.                     data_bytes ++;
  400.                 }
  401.                 /* DROP ON THROUGH */
  402.                 default:
  403.                 newline = FALSE;
  404.             }
  405.             if (ferror (sm_wfp))
  406.                 break;
  407.             /* finally send the data character */
  408.             putc ((lastchar = *bufptr), sm_wfp);
  409.             data_bytes ++;
  410.             if (ferror (sm_wfp))
  411.                 break;
  412.         }
  413.         retval = ferror(sm_wfp) ? RP_DHST : RP_OK;
  414.     }
  415.  
  416.     return (retval);
  417. }
  418.  
  419. static int start_conn (sp, name, errstr)
  420. struct sockaddr_in *sp;
  421. char    *name;
  422. char    *errstr;
  423. {
  424.     int s;
  425.  
  426.     s = socket (AF_INET, SOCK_STREAM, 0);
  427.     if( s < 0 ) {
  428.         (void) strcpy (errstr, "Can't get socket");
  429.         PP_SLOG (LLOG_EXCEPTIONS, "socket",
  430.              ("%s", errstr));
  431.         return NOTOK;
  432.     }
  433.  
  434.     if (timeout(SM_OTIME)) {
  435.         (void) sprintf (errstr, "[%s] open timeout", name,
  436.                 inet_ntoa (sp -> sin_addr));
  437.         PP_LOG (LLOG_EXCEPTIONS,
  438.             ("%s", errstr));
  439.         return NOTOK;
  440.     }
  441.     
  442.     if( connect (s, (struct sockaddr *)sp, sizeof *sp) < 0 ) {
  443.         (void) close (s);
  444.         (void) sprintf (errstr, "Connection failed to %s [%s]",
  445.                 name,  inet_ntoa (sp -> sin_addr));
  446.         PP_SLOG (LLOG_EXCEPTIONS, "connect",
  447.              ("%s", errstr));
  448.         timeout (0);
  449.         return NOTOK;
  450.     }
  451.     timeout (0);
  452.     return  s;
  453. }
  454.  
  455. sm_nopen(hostnam, errstr)
  456. char    *hostnam;
  457. char    *errstr;
  458. {
  459.     int    fds[2], skt;
  460.     short    retval;
  461.     char    linebuf[LINESIZE];
  462.     static char *us, *official();
  463.     extern int smtpport;
  464.     struct hostent *hp, *gethostbyname ();
  465.     struct sockaddr_in s_in;
  466.     char **aptr;
  467.  
  468.     PP_TRACE (("[ %s ]", hostnam));
  469.  
  470.     *errstr = 0;
  471.  
  472.     if ( ! us ) {
  473.         us = official (loc_dom_mta);
  474.         if(us == NULLCP) {
  475.             PP_OPER (NULLCP, 
  476.                  ("Cannot find my own 'official' name '%s'\n",
  477.                   loc_dom_mta));
  478.             (void) sprintf (errstr, "Who am I %s ?", loc_dom_mta);
  479.             return RP_BHST;
  480.         }
  481.     }
  482. #ifdef NAMESERVER
  483.     if (rp_isbad (retval = ns_gethost (hostnam, &hp, errstr)))
  484.         return (retval);
  485. #else NAMESERVER
  486.     if ((hp = gethostbyname (hostnam)) == NULL) {
  487.         (void) sprintf (errstr, "Host lookup of %s failed", hostnam);
  488.         return RP_BHST;
  489.     }
  490. #endif NAMESERVER
  491.  
  492.     if (smtpport == 0) {
  493.         struct servent *sp;
  494.  
  495.         if ((sp = getservbyname ("smtp", "tcp")) == NULL) {
  496.             (void) sprintf (errstr, "Can't determine smtp port");
  497.             return RP_AGN;
  498.         }
  499.         smtpport = (short)sp->s_port;
  500.     }
  501.     bzero ((char *)&s_in, sizeof s_in);
  502.     s_in.sin_family = AF_INET;
  503.     s_in.sin_port = smtpport;
  504.  
  505.  
  506.             /* SEK - try all addresses */
  507.     for (aptr = hp -> h_addr_list; *aptr; aptr++)
  508.     {
  509.         bcopy (*aptr, (char *)&s_in.sin_addr, hp -> h_length); 
  510.         PP_NOTICE (("Attempting open to [%s]", 
  511.                  inet_ntoa (s_in.sin_addr)));
  512.  
  513.         if ((skt = start_conn (&s_in, hp -> h_name, errstr)) == NOTOK)
  514.             continue;
  515.  
  516.         fds[0] = skt;
  517.         fds[1] = dup (skt);
  518.  
  519.         if ((sm_rfp = fdopen (fds[0], "r")) == NULL ||
  520.             (sm_wfp = fdopen (fds[1], "w")) == NULL) {
  521.             return (RP_LIO);
  522.         }
  523.  
  524.         if (timeout(SM_ATIME)) {
  525.             (void) sprintf (errstr, "Timeout opening %s", hostnam);
  526.             sm_nclose (NOTOK);
  527.             return( RP_BHST );
  528.         }
  529.         if (rp_isbad (retval = sm_irdrply ())) {
  530.             (void) sprintf (errstr, "Bad response from %s: %s",
  531.                     hostnam, smtp_error ("bad reply"));
  532.             timeout (0);
  533.             sm_nclose (NOTOK);
  534.             return(retval);
  535.         }
  536.         timeout (0);
  537.  
  538.         if( sm_rp.sm_rval != 220 ) {
  539.             sm_nclose (NOTOK);
  540.             return( RP_BHST );
  541.         }
  542.  
  543.         /* HELO is part of protocol - all commands are 4 letters long */
  544.         (void) sprintf (linebuf, "HELO %s", us);
  545.         if (rp_isbad (sm_cmd( linebuf, SM_HTIME))
  546.             || sm_rp.sm_rval != 250 ) {
  547.             (void) sprintf (errstr, "Bad response to helo: %s",
  548.                     smtp_error("bad helo"));
  549.             sm_nclose (NOTOK);
  550.             return (RP_RPLY);
  551.         }
  552.         PP_NOTICE (("Connected to %s [%s]", hostnam,
  553.                 inet_ntoa (s_in.sin_addr)));
  554.         return (RP_OK);
  555.     }
  556.     return (RP_BHST);  /* No addresses OK */
  557.  
  558. }
  559.  
  560. sm_nclose (type)        /* end current connection */
  561. short      type;            /* clean or dirty ending */
  562. {
  563.     if (type == OK && sm_wfp)
  564.         sm_cmd ("QUIT", SM_QTIME);
  565.  
  566.     if (sm_rfp == NULL && sm_wfp == NULL)
  567.         return;
  568.     PP_NOTICE (("Closing connection to %s", open_host));
  569.     if (open_host)
  570.         free(open_host);
  571.     open_host = NULLCP;
  572.  
  573.     if (timeout(15)) {
  574.         return;
  575.     }
  576.     if (sm_rfp != NULL)
  577.         (void) fclose (sm_rfp);
  578.     if (sm_wfp != NULL)
  579.         (void) fclose (sm_wfp);
  580.     timeout (0);
  581.     sm_rfp = sm_wfp = NULL;
  582. }
  583.  
  584. static char    *official (name)
  585. char    *name;
  586. {
  587.     struct hostent *hp;
  588. #ifdef NAMESERVER
  589.     int    i;
  590.  
  591.         for(i = 0 ; i < 10 ; i++){
  592.                 hp = gethostbyname(name);
  593.                 if(hp != NULL)
  594.                         break;
  595.         PP_TRACE (("Timeout looking up %s", name));
  596.                 if(i > 2)
  597.                         sleep((unsigned) i*2);
  598.         }
  599. #else
  600.     hp = gethostbyname(name);
  601. #endif
  602.     return hp == NULL ? NULLCP : strdup (hp -> h_name);
  603. }
  604.